package org.bringer.webapp.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.tapestry5.OptionGroupModel;
import org.apache.tapestry5.OptionModel;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.internal.OptionModelImpl;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.ioc.services.PropertyAdapter;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.util.AbstractSelectModel;

public class SelectSupport <T> extends AbstractSelectModel implements ValueEncoder<T> {
	   private final List<T> items;
	   private Map<String, PropertyAdapter> adapterMap = new HashMap<String, PropertyAdapter>();
	   private String label;
	   private PropertyAdapter indexPropertyAdapter;
	   private TypeCoercer typeCoercer;
	   private final static Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{([\\w.$]+)\\}");

	   public SelectSupport(final List<T> items, final String label, 
	         String indexProperty,
	         final Class<?> valueType,
	         final PropertyAccess access, TypeCoercer typeCoercer) {
	      this.items = items;
	      this.label = label;
	      indexPropertyAdapter = access.getAdapter(valueType).getPropertyAdapter(indexProperty);
	      Matcher matcher = PROPERTY_PATTERN.matcher(label);
	      this.typeCoercer = typeCoercer;
	      while (matcher.find()) {
	         adapterMap.put(matcher.group(0),
	               access.getAdapter(valueType).getPropertyAdapter(matcher.group(1)));
	      }
	   }

	   public List<OptionGroupModel> getOptionGroups() {
	      return null;
	   }

	   public List<OptionModel> getOptions() {
	      final List<OptionModel> options = new ArrayList<OptionModel>();

	      if (items != null) {
	         for(T item: items){
	            options.add(new OptionModelImpl(toLabel(item), item));
	         }
	      }

	      return options;
	   }

	   private String toLabel(Object object) {
	      String label = this.label;
	      for (String key : adapterMap.keySet()) {
	         label = label.replace(key, 
	               adapterMap.get(key).get(object).toString());
	      }
	      return label;
	   }

	   /**
	    * {@inheritDoc}
	    */
	   public String toClient(T value) {
	      return typeCoercer.coerce(getIndex(value), String.class);
	   }

	   /**
	    * {@inheritDoc}
	    */
	   @SuppressWarnings("unchecked")
	   public T toValue(String clientValue) {
	      for(T value: items){
	         if(getIndex(value).equals(typeCoercer.coerce(clientValue, indexPropertyAdapter.getType()))){
	            return value;
	         }
	      }
	      
	      return null;
	   }
	   
	   /**
	    * Gets the index value for a particular item
	    * 
	    * @param value
	    */
	   private Object getIndex(Object value){
	      Object fieldValue = indexPropertyAdapter.get(value);
	      if(fieldValue == null){
	         throw new RuntimeException("Index property cannot be null");
	      }
	      return fieldValue;
	   }
}
